/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package pfc.generacion;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.ddlutils.model.Column;
import org.apache.ddlutils.model.Database;
import org.apache.ddlutils.model.Table;
import pfc.comun.Proyecto;
import pfc.modelocg.CGModel;
import pfc.modeloer.ERAttrib;
import pfc.modeloer.EREntity;
import pfc.modeloer.ERModel;
import pfc.modeloer.ERReference;
import pfc.modeloer.ERRelationShip;

/**
 *
 * @author xandru
 */
public class GeneradorBO {

    Proyecto _proyecto;
    Database _dbmodel;
    ERModel _erModel;
    CGModel _cgModel;

    public GeneradorBO(Proyecto pro, Database db, ERModel er, CGModel cg) {
        this._proyecto = pro;
        this._dbmodel = db;
        this._erModel = er;
        this._cgModel = cg;
    }

    public void generarBOs(File srcPath) {
        File f = new File(srcPath.getAbsolutePath() + File.separator + "bo");

        try {
            FileUtils.forceMkdir(f);
        } catch (IOException ex) {
            Logger.getLogger(Application.class.getName()).log(Level.SEVERE, null, ex);
        }

        generarBOExceptions(f);

        generarBOBase(f);
        generarBOFactory(f);

        for (EREntity e : _erModel.getEntityList()) {
            generarBOFile(f, e);
        }
    }

    private void generarBOFile(File f, EREntity e) {

        String clase = StringUtils.capitalize(e.getName() + "BO");
        String claseDTO = StringUtils.capitalize(e.getName());
        

        ArrayList<String> imports = new ArrayList<String>();
        ArrayList<String> fields = new ArrayList<String>();
        ArrayList<String> cl = new ArrayList<String>();
        ArrayList<String> methods = new ArrayList<String>();

        imports.add("package " + _proyecto.nombre + ".bo;\n");

        addImports(imports);
        addFields(fields);
        addConstructor(methods, clase, claseDTO);
        addGetReferencedEntityMethods(methods, e);
        addGetReferredEntitiesMethods(methods,e);
        if (e.getErElementType()==EREntity.EntityType.Master)
        {
            addGetAll(methods,e);
        }
        
        try {
            PrintWriter dto = new PrintWriter(new FileOutputStream(f.getAbsolutePath() + File.separator + clase + ".java"));

            for (String line : imports) {
                dto.println(line);
            }
            dto.println("public class " + clase + " extends BaseBO{");
            for (String line : fields) {
                dto.println(line);
            }
            for (String line : methods) {
                dto.println(line);
            }
            dto.println("}");

            dto.flush();
            dto.close();
        } catch (FileNotFoundException ex) {
            Logger.getLogger(Application.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private void addConstructor(ArrayList<String> metodos, String clase, String claseDTO) {
        metodos.add("public " + clase + "( DataSource ds ) {");
        metodos.add("this.dataSource = ds;");
        metodos.add("}");
        metodos.add("\n");
    }

    private void addImports(ArrayList<String> imports) {
        String[] aImportar = {_proyecto.nombre + ".dto.*",
                              _proyecto.nombre + ".dao.*",
                              "javax.sql.DataSource",
                              "java.sql.SQLException",
                              "java.util.ArrayList",
                              "java.util.List",
                              "pfc.core.db.*"};

        for (String p: aImportar)
        {
            imports.add("import " + p + ";");
        }
        imports.add("\n");

    }

    private void generarBOExceptions(File f) {
        /*
        String[] exceptions = {"CreateException", "DuplicateKeyException", "FinderException", "ObjectNotFoundException", "NoSuchEntityException"};
        try {
            for (String ex : exceptions) {
                PrintWriter exf = new PrintWriter(new FileOutputStream(f.getAbsolutePath() + File.separator + ex + ".java"));
                exf.println("package " + _proyecto.nombre + ".dao;");
                exf.println();
                exf.println("public class " + ex + " extends Exception {\n");
                exf.println("public " + ex + "(String message) {\n");
                exf.println("super(message);");
                exf.println("}");
                exf.println("}\n");
                exf.flush();
                exf.close();
            }
        } catch (FileNotFoundException ex) {
            Logger.getLogger(GeneradorBO.class.getName()).log(Level.SEVERE, null, ex);
        }
         *
         */
    }

    private void addGetReferencedEntityMethods(ArrayList<String> methods, EREntity e) {
                
        for (ERAttrib  a: e.getAttribs())
        {
            for (ERReference ref: this._erModel.getReferencedByAttrib(e, a)) {
                addGetReferencedEntity(ref, methods);   
            }
          
        }
    }

    private void addFields(ArrayList<String> fields) {
        fields.add("DataSource dataSource;");
    }

   

    

    private void addGetReferencedEntity(ERReference ref, ArrayList<String> methods) {
        String sourceEntityDTOClass = StringUtils.capitalize(ref.getSourceEntityName());
        String sourceEntityDAOClass = sourceEntityDTOClass + "DAO";
        String targetEntityDTOClass = StringUtils.capitalize(ref.getTargetEntityName());
        String targetEntityDAOClass = targetEntityDTOClass + "DAO";
        String sourceAttrib = ref.getSourceAttribName();
        String targetAttrib = ref.getTargetAttribName();

        methods.add("public " + targetEntityDTOClass + " get"+targetEntityDTOClass+"(" + sourceEntityDTOClass + " obj) throws java.text.ParseException {");

        methods.add(targetEntityDTOClass + " resul = null;");
        methods.add(targetEntityDTOClass + " util = new " + targetEntityDTOClass + "();");
        methods.add("String pkAttrib = util.getIdentity().getField();");
        methods.add("util.putPropertyValue(pkAttrib,obj.stringPropertyValue(\""+ sourceAttrib +"\"));");

        methods.add(targetEntityDAOClass + " objeto = new " + targetEntityDAOClass + "( dataSource );");
        methods.add("resul = objeto.findByPrimaryKey(util.getIdentity());");
        methods.add("return resul;");

        methods.add("}");
    }

    private void addGetReferredEntitiesMethods(ArrayList<String> methods, EREntity e) {

        //System.out.println("\n-----------------------------\n");
        //System.out.println(e.getName().toUpperCase());
        for (ERAttrib  a: e.getAttribs())
        {
            //System.out.println("Buscando entidades que referencian " + e.getName() + "." + a.getName().toUpperCase());
            for (ERReference ref: this._erModel.getReferrerToAttrib(e, a)) {
                //System.out.println("\t" + ref.toString());
                addGetRefererEntities(ref, methods);
            }

        }
    }

    private void addGetRefererEntities(ERReference ref, ArrayList<String> methods) {
        String sourceEntityDTOClass = StringUtils.capitalize(ref.getSourceEntityName());
        String sourceEntityDAOClass = sourceEntityDTOClass + "DAO";
        String targetEntityDTOClass = StringUtils.capitalize(ref.getTargetEntityName());
        String targetEntityDAOClass = targetEntityDTOClass + "DAO";
        String sourceAttrib = ref.getSourceAttribName();
        String targetAttrib = ref.getTargetAttribName();

        methods.add("public Iterable<" + sourceEntityDTOClass + "> get"+sourceEntityDTOClass+"List(" + targetEntityDTOClass + " obj) throws java.text.ParseException {");

        methods.add("List<" + sourceEntityDTOClass + "> resul = null;");
        methods.add(sourceEntityDAOClass + " objeto = new " + sourceEntityDAOClass + "( dataSource );");
        methods.add(sourceEntityDTOClass + " util = new " + sourceEntityDTOClass + "();");
        methods.add("util.putPropertyValue(\""+ sourceAttrib +"\",obj.stringPropertyValue(\""+targetAttrib+ "\"));");
        methods.add("resul = objeto.find" + sourceEntityDTOClass +"List( util );");
        methods.add("return resul;");

        methods.add("}");
    }

    private void addGetAll(ArrayList<String> methods, EREntity e) {
        String entityDTOClass = StringUtils.capitalize(e.getName());
        String entityDAOClass  = entityDTOClass + "DAO";
        methods.add("public Iterable<" + entityDTOClass + "> getAll() throws java.text.ParseException {");

        methods.add("List<" + entityDTOClass + "> resul = null;");
        methods.add(entityDAOClass + " objeto = new " + entityDAOClass + "( dataSource );");
        methods.add("resul = objeto.getAll();");
        methods.add("return resul;");

        methods.add("}");
    }

    private void generarBOFactory(File f) {
        String clase = "BOFactory";

        try {
            PrintWriter bo = new PrintWriter(new FileOutputStream(f.getAbsolutePath() + File.separator + clase + ".java"));

            bo.println("package " + _proyecto.nombre + ".bo;\n");

            bo.println("import javax.sql.DataSource;");
            bo.println("import pfc.core.db.DataSourceFactory;");

            bo.println("public class " + clase + " {");

            bo.println("public static BaseBO getBO(String clase) {");
            bo.println("DataSource ds = DataSourceFactory.getDataSourceFromJNDI(\"jdbc/" + this._proyecto.getNombre() +"\");");
            for (Table t: this._dbmodel.getTables())
            {
                if (t.getName().startsWith("_"))
                    continue;
                String boClass = StringUtils.capitalize(t.getName()) + "BO";
                bo.println("if (clase.equals(\"" + boClass + "\")) return new " + boClass + "(ds);" );
            }
            bo.println("return null;");
            bo.println("}");

            bo.println("public static BaseBO getBO(String clase, String jndiName) {");
            bo.println("DataSource ds = DataSourceFactory.getDataSourceFromJNDI(jndiName);");
            for (Table t: this._dbmodel.getTables())
            {
                if (t.getName().startsWith("_"))
                    continue;
                String boClass = StringUtils.capitalize(t.getName()) + "BO";
                bo.println("if (clase.equals(\"" + boClass + "\")) return new " + boClass + "(ds);" );
            }
            bo.println("return null;");
            bo.println("}");


            bo.println("}");
            bo.flush();
            bo.close();
        } catch (FileNotFoundException ex) {
            Logger.getLogger(Application.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private void generarBOBase(File f) {
        String clase = "BaseBO";

        try {
            PrintWriter bo = new PrintWriter(new FileOutputStream(f.getAbsolutePath() + File.separator + clase + ".java"));

            bo.println("package " + _proyecto.nombre + ".bo;\n");

            bo.println("public abstract class " + clase + " {");
            bo.println("}");
            bo.flush();
            bo.close();
        } catch (FileNotFoundException ex) {
            Logger.getLogger(Application.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}
